﻿using Newtonsoft.Json;
using SqlSugar;
using System.Reflection;
using Wechat_PublicNumber.Common;

namespace Wechat_PublicNumber
{
    [Injection(DIPattern.Scoped, InitMethod = nameof(InitDb))]
    public class DataAccess : SqlSugarExternalFuc
    {
        [Autowired]
        public ScopeLog _logger;

        [Autowired]
        public Page _page;

        public SqlSugarScopeProvider WXDb => GetSqlSugarClient(nameof(WXDb));

        private SqlSugarScope dbScope;

        private SqlSugarScopeProvider GetSqlSugarClient(string dbKey)
        {
            if (dbScope is null) InitDb();

            return dbScope.GetConnectionScope(dbKey);
        }

        private void InitDb()
        {
            List<ConnectionConfig> connList = new List<ConnectionConfig>();

#if DEBUG
            var settingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Setting/DBSettingDebugger.json");
#else
            var settingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Setting/DBSetting.json");
#endif
            if (File.Exists(settingPath))
            {
                var jsonString = File.ReadAllText(settingPath);
                try
                {
                    var dbSettings = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
                    foreach (var db in dbSettings)
                    {
                        connList.Add(new ConnectionConfig()
                        {
                            ConfigId = db.Key,
                            DbType = DbType.MySql,
                            ConnectionString = db.Value,
                            IsAutoCloseConnection = true,
                            ConfigureExternalServices = new ConfigureExternalServices()
                            {
                                SqlFuncServices = GetexpMethods()
                            }
                        });
                    }
                    dbScope = new SqlSugarScope(connList, db =>
                    {
                        foreach (var ds in dbSettings)
                        {
                            db.GetConnectionScope(ds.Key).Aop.OnLogExecuted = (sql, pars) =>//执行SQL 结束后
                            {
                                foreach (var par in pars.OrderByDescending(x => x.ParameterName))
                                {
                                    sql = sql.Replace(par.ParameterName.ToString(), par.DbType.ToString().ToUpper().Contains("INT") ? par.Value?.ToString() : "'" + par.Value?.ToString() + "'");
                                }
                                _logger.Sql($"The sql executed is \n{sql.Replace("`", "")}");
                            };

                            db.GetConnectionScope(ds.Key).Aop.OnError = (exp) =>//执行SQL 错误事件
                            {
                                List<Error> errors = JsonConvert.DeserializeObject<List<Error>>(JsonConvert.SerializeObject(exp.Parametres));
                                foreach (var par in errors.OrderByDescending(x => x.ParameterName))
                                {
                                    exp.Sql = exp.Sql.Replace(par.ParameterName.ToString(), par.DbType.ToString().ToUpper().Contains("INT") ? par.Value?.ToString() : "'" + par.Value?.ToString() + "'");
                                }

                                _logger.Error($"The sql executed is \n{exp.Sql.Replace("`", "")} \n Messag:{exp.Message}");
                            };
                        }
                    });
                }
                catch { }
            }
        }

        private class Error
        {
            public int _Size { get; set; }

            public string IsRefCursor { get; set; }

            public int DbType { get; set; }

            public int Direction { get; set; }

            public string IsNullable { get; set; }

            public string ParameterName { get; set; }

            public int Size { get; set; }

            public string SourceColumn { get; set; }

            public string SourceColumnNullMapping { get; set; }

            public string UdtTypeName { get; set; }

            public string Value { get; set; }

            public string TempDate { get; set; }

            public int SourceVersion { get; set; }

            public string TypeName { get; set; }

            public int Precision { get; set; }

            public int Scale { get; set; }
        }
    }

    public static class DbExtensions
    {
        /// <summary>
        /// 添加和修改通用方法，并且返回ID 必须加上该特性
        /// [SugarColumn(IsPrimaryKey = true)]
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static async Task<long> SaveEntityAsync<T>(this SqlSugarScopeProvider db, T entity) where T : class, new()
        {
            var primaryProperty = entity.GetType().GetProperties().Where(s => s.GetCustomAttribute<SugarColumn>()?.IsPrimaryKey == true).FirstOrDefault();

            var primaryValue = (long)primaryProperty.GetValue(entity, null);

            if (primaryValue == 0)
                primaryValue = await db.Insertable(entity).ExecuteReturnBigIdentityAsync();
            else
                await db.Updateable(entity).ExecuteCommandAsync();

            return primaryValue;
        }
    }

    public static class DbStatic
    {
        public static SqlSugarScopeProvider WXDb => GetSqlSugarClient(nameof(WXDb));

        private static SqlSugarScope dbScope;

        private static SqlSugarScopeProvider GetSqlSugarClient(string dbKey)
        {
            if (dbScope is null) InitDb();

            return dbScope.GetConnectionScope(dbKey);
        }

        private static void InitDb()
        {
            List<ConnectionConfig> connList = new List<ConnectionConfig>();

#if DEBUG
            var settingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Setting/DBSettingDebugger.json");
#else
            var settingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Setting/DBSetting.json");
#endif
            if (File.Exists(settingPath))
            {
                var jsonString = File.ReadAllText(settingPath);
                try
                {
                    var dbSettings = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
                    foreach (var db in dbSettings)
                    {
                        connList.Add(new ConnectionConfig()
                        {
                            ConfigId = db.Key,
                            DbType = DbType.MySql,
                            ConnectionString = db.Value,
                            IsAutoCloseConnection = true
                        });
                    }
                    dbScope = new SqlSugarScope(connList, db =>
                    {
                        foreach (var ds in dbSettings)
                        {
                            db.GetConnectionScope(ds.Key).Aop.OnLogExecuted = (sql, pars) =>//执行SQL 结束后
                            {
                                foreach (var par in pars.OrderByDescending(x => x.ParameterName))
                                {
                                    sql = sql.Replace(par.ParameterName.ToString(), par.DbType.ToString().ToUpper().Contains("INT") ? par.Value?.ToString() : "'" + par.Value?.ToString() + "'");
                                }
                            };

                            db.GetConnectionScope(ds.Key).Aop.OnError = (exp) =>//执行SQL 错误事件
                            {
                                List<Error> errors = JsonConvert.DeserializeObject<List<Error>>(JsonConvert.SerializeObject(exp.Parametres));
                                foreach (var par in errors.OrderByDescending(x => x.ParameterName))
                                {
                                    exp.Sql = exp.Sql.Replace(par.ParameterName.ToString(), par.DbType.ToString().ToUpper().Contains("INT") ? par.Value?.ToString() : "'" + par.Value?.ToString() + "'");
                                }
                            };
                        }
                    });
                }
                catch { }
            }
        }

        private class Error
        {
            public int _Size { get; set; }

            public string IsRefCursor { get; set; }

            public int DbType { get; set; }

            public int Direction { get; set; }

            public string IsNullable { get; set; }

            public string ParameterName { get; set; }

            public int Size { get; set; }

            public string SourceColumn { get; set; }

            public string SourceColumnNullMapping { get; set; }

            public string UdtTypeName { get; set; }

            public string Value { get; set; }

            public string TempDate { get; set; }

            public int SourceVersion { get; set; }

            public string TypeName { get; set; }

            public int Precision { get; set; }

            public int Scale { get; set; }
        }
    }
}
